package com.comqing.comqizhi.aiops.application.taskscheduler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledFuture;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.stereotype.Component;

import com.comqing.comqizhi.aiops.application.dto.regularwork.PlanDto;
import com.comqing.comqizhi.aiops.application.service.RegularWorkTaskService;
import com.comqing.comqizhi.aiops.application.service.WorkPlanSetService;

import cn.hutool.core.collection.CollectionUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 定期工作任务的生成
 * 
 * @author zhangc
 * @version 1.0
 * @description TODO
 * @date 2024-02-27 20:30
 */
@Component
@Slf4j
public class ScheduleSetting implements SchedulingConfigurer, AsyncConfigurer {

    @Autowired
    private WorkPlanSetService workPlanSetService;
    @Autowired
    private RegularWorkTaskService regularWorkTaskService;
    private volatile ScheduledTaskRegistrar scheduledTaskRegistrar;
    // 调度器map
    private final Map<Long, ScheduledFuture<?>> scheduledFutures = new HashMap<Long, ScheduledFuture<?>>();
    // 执行任务map
    private final Map<Long, CronTask> cronTasks = new HashMap<Long, CronTask>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        // 异步执行定时任务
        TaskScheduler taskScheduler = taskScheduler();
        scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
        this.scheduledTaskRegistrar = scheduledTaskRegistrar;
        refreshTask();
    }

    /**
     * 全量刷新所有任务
     */
    public void refreshTask() {
        List<PlanDto> scheduleList = workPlanSetService.getAllPlan();
        // redisUtil.setSysScheduledList("scheduleList", scheduleList);
        refresh(scheduleList);
    }

    public void deleteTask(Long keyId) {
        try {
            // 任务调度时间发生改变，取消当前策略的任务
            scheduledFutures.get(keyId).cancel(false);
            scheduledFutures.remove(keyId);
            cronTasks.remove(keyId);
        } catch (Exception e) {
            log.error("deleteTask error,{}", keyId, e);
        }
    }

    public void createTask(PlanDto sysScheduled) {
        // 新建定时任务
        CronTask task = new CronTask(getRunnable(sysScheduled), sysScheduled.getWorkPeriodCron());
        // 将定时任务分配到对应的调度器
        ScheduledFuture<?> scheduledFuture = Objects.requireNonNull(scheduledTaskRegistrar.getScheduler())
            .schedule(task.getRunnable(), task.getTrigger());
        // 保存缓存任务map
        cronTasks.put(sysScheduled.getId(), task);
        // 保存缓存调度器 map
        scheduledFutures.put(sysScheduled.getId(), scheduledFuture);
    }

    public void refresh(List<PlanDto> scheduleList) {
        // 取消停用的定时策略
        Set<Long> keyIds = scheduledFutures.keySet();
        CopyOnWriteArrayList<Long> copyKeyIds = CollectionUtil.newCopyOnWriteArrayList(keyIds);

        for (Long scheduledFutureId : copyKeyIds) {
            if (!taskExit(scheduleList, scheduledFutureId)) {
                // cancel 参数 false 表示如果调度器当前执行任务时 不进行打断 true表示直接打断进程取消策略
                scheduledFutures.get(scheduledFutureId).cancel(false);
                scheduledFutures.remove(scheduledFutureId);
                cronTasks.remove(scheduledFutureId);
            }
        }
        if (CollectionUtil.isEmpty(scheduleList)) {
            return;
        }
        for (PlanDto plan : scheduleList) {
            // 定时corn表达式
            String cron = plan.getWorkPeriodCron();
            Long keyId = plan.getId();
            if (StringUtils.isBlank(cron)) {
                continue;
            }
            // 定时任务存在的话，并且没发生改变的话不进行处理
            if (scheduledFutures.containsKey(keyId) && cronTasks.get(keyId).getExpression().equalsIgnoreCase(cron)) {
                continue;
            } else if (scheduledFutures.containsKey(keyId)) {
                deleteTask(keyId);
            }
            createTask(plan);
        }
    }

    /**
     * 判断调度器缓存map中是否存在查询到的配置调度任务
     *
     * @param scheduleList 数据库查询的调度任务
     * @param taskId 主键id
     * @return 判断结果
     */
    private boolean taskExit(List<PlanDto> scheduleList, Long taskId) {
        if (CollectionUtil.isEmpty(scheduleList) || taskId == null) {
            return false;
        }
        for (PlanDto sysScheduled : scheduleList) {
            // id存在 且任务状态是开启状态 判断任务存在
            if (taskId.equals(sysScheduled.getId())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 设置异步线程池 任务调度器
     *
     * @return 返回任务调度器对象
     */
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.initialize();
        // 设置线程池数量
        scheduler.setPoolSize(8);
        scheduler.setThreadNamePrefix("regular-task-create");
        // 等待终止时间
        scheduler.setAwaitTerminationSeconds(60);
        // 等待任务完成关闭
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        return scheduler;
    }

    private Runnable getRunnable(final PlanDto plan) {
        return () -> {
            try {
                log.info("创建任务数据,id:{}", plan.getId());
                regularWorkTaskService.timerAdd(plan.getId().toString());
            } catch (Exception ex) {
                log.error("创建任务失败", ex);
            }
        };
    }
}
